package jers.impl;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import jers.Callback;
import jers.JERS;
import jers.Result;
import jers.util.NetworkUtil;
import jers.util.Utility;

/**
 * Classe que implementa a interface {@link Callback}.
 * <p>
 * Esta extende a classe {@link UnicastRemoteObject} para prover 
 * as chamadas de retorno dos servico disparados remotamente via RMI.
 * <br>
 * Esta implementa um decorador para a callback do cliente, nao sendo 
 * necessario que este conheca os detalhes da comunicacao.
 * </p>
 * 
 * @see {@link UnicastRemoteObject}, {@link SystemFactory}, {@link Callback}
 * 
 * @author 
 * 		<a href="mailto:felipe.zappala@gmail.com">
 * 			Luis Felipe Zappala
 * 		</a> [ <a href="http://lncc.br/ist">ISTCC-P</a> / 2009 ]
 */
final class RemoteProxyCallback extends UnicastRemoteObject 
		implements Callback {

	/** O ID serial da versao da classe */
	private static final long serialVersionUID = 303913169969444285L;
	
	private Callback delegateCallback;
	
	protected RemoteProxyCallback(Callback delegate) throws RemoteException {
		super();
		if (delegate == null) {
			throw new IllegalArgumentException(
					"Validation error: parameter Callback is null");
		}
		this.delegateCallback = delegate;
	}

	public void call(Result result) throws RemoteException {
		try {
			this.delegateCallback.call(result);
			
		} catch (Exception e) {
			throw new RemoteException("Callback proxy error", e);
		}
	}

	public static Callback getInstance(Callback callback) {
		try {
			// Gambiarra para manter a jvm da callback ativa pra resposta
			Callback delegate = new WaiterCallback(callback);
			
			// Obtem a porta da callback, se estiver em uso, gera uma aleatoria
			int port = NetworkUtil.getFreePort(JERS.CALLBACK_PORT);

			JERS.log.info("[RemoteProvider] Setup callback system on port [", 
					port, "] for class [", callback.getClass().getName(), "]");
			
			// Obtem a instancia da callback, o decorador remoto.
			Callback remoteCallback = new RemoteProxyCallback(delegate);
			
			// Remove um possivel registro anterior desta instancia.
			UnicastRemoteObject.unexportObject(remoteCallback, true);
			
			// Obtem a instancia stub da callback, o objeto proxy do RMI,
			// e exporta a callback para o registrador
			Callback stub = (Callback) UnicastRemoteObject
					.exportObject(remoteCallback, port);
			
			// Cria o registrador na porta RMI configurada.
			Registry rmiregistry = LocateRegistry.createRegistry(port);
			
			// Reassocia o nome do servico ao stub proxy
			rmiregistry.rebind(JERS.CALLBACK_NAME, stub);
	
			String uri = Utility.concat(
					"rmi://localhost:", port, "/", JERS.CALLBACK_NAME); 
			
			JERS.log.info("[RemoteProxyCallback] Callback RMI address: ", uri);
			
			// Para funcionar corretamente deveria ser Activable 
			Callback proxyCallback = (Callback) Naming.lookup(uri);
			
			return proxyCallback;
			
		} catch (Exception e) {
			throw new RuntimeException("Callback proxy error", e);
		}
	}
	
}
